一文讲透java弱引用以及使用场景 您所在的位置:网站首页 一文讲透Java Agent是什么玩意能干啥怎么用 一文讲透java弱引用以及使用场景

一文讲透java弱引用以及使用场景

2024-06-02 18:20| 来源: 网络整理| 查看: 265

文章目录 概念深入原理应用案例解析

概念

大部分情况下我们看到是强引用,比如下面这一行:

String str1 = new String("abc");

变量str1被用来存放一个string对象的强引用上。强引用在你正在使用时这个对象时,一般是不会被垃圾回收器回收的。当出现内存空间不足时,虚拟机不会释放强引用的对象占用的空间,而是选择抛出异常(OOM)。

什么时候会回收强引用的空间呢,就是没有引用的时候,比如你这样写:

str1 = null

GC在适当的时候就会回收str1指向的空间。

而弱引用(Weak Reference)的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的时,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。

java中使用弱引用的语法是:

String str1 = new String("abc"); WeakReference weakReference = new WeakReference(str1); 深入原理

我们先来通过一个案例,看下gc对于弱引用的回收策略。

public class App { public static WeakReference weakReference1; public static void main(String[] args) { test1(); //test1外部,hello对象作用域结束,没有强引用指向"value"了。只有一个弱引用指向"value" System.out.println("未进行gc时,只有弱引用指向value内存区域:" + weakReference1.get()); //此时gc时会回收弱引用 System.gc(); //此时输出都为nuill System.out.println("进行gc时,只有弱引用指向value内存区域:" + weakReference1.get()); } public static void test1() { //hello对象强引用"value" String hello = new String("value"); //weakReference1对象弱引用指向"value" weakReference1 = new WeakReference(hello); //在test1内部调用gc,此时gc不会回收弱引用,因为hello对象强引用"value" System.gc(); System.out.println("进行gc时,强引用与弱引用同时指向value内存区域:" + weakReference1.get()); } }

运行的结果:

进行gc时,强引用与弱引用同时指向value内存区域:value 未进行gc时,只有弱引用指向value内存区域:value 进行gc时,只有弱引用指向value内存区域:null

这里有个前置知识说下,当要获得WeakReference的object时, 首先需要判断它是否已经被GC回收,若被收回,则下列返回值为空:

weakReference1.get();

根据这个结果,我们可以得出这样的结论:

当有强引用指向value内存区域时,即使进行gc,弱引用也不会被释放,对象空间不回被回收。

当无强引用指向value内存区域是,进行gc,弱引用会被释放,对象空间将会执行回收流程。

我们接着从源码层面看下弱引用。首先引入一个概念叫gc的可达性。因为本篇文章并不是专门讲gc的,所以我这里并不打算展开太多这部分,知识一句话概括下gc可达性的概念。

GC决定一个对象是否可被回收,其基本思路是从GC Root开始向下搜索,如果对象与GC Root之间存在引用链,则对象是可达的,GC会根据是否可到达与可到达性决定对象是否可以被回收。

public class WeakReference extends Reference { /** * Creates a new weak reference that refers to the given object. The new * reference is not registered with any queue. * * @param referent object the new weak reference will refer to */ public WeakReference(T referent) { super(referent); } /** * Creates a new weak reference that refers to the given object and is * registered with the given queue. * * @param referent object the new weak reference will refer to * @param q the queue with which the reference is to be registered, * or null if registration is not required */ public WeakReference(T referent, ReferenceQueue k, Object v) { super(k); value = v; } }

然后你会注意到,Entry的Key即ThreadLocal对象是采用弱引用引入的。为什么ThreadLocalMap使用弱引用存储ThreadLocal呢?

还是看上面那张图。

ThreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal没有外部强引用来引用它,那么系统 GC 的时候,这个ThreadLocal会被回收,这样一来,ThreadLocalMap中就会出现key为null的Entry,就没有办法访问这些key为null的Entry的value,如果当前线程迟迟不结束的话(因为大部分时候我们用的都是线程池,核心线程都是长期驻留的),这些key为null的Entry的value就会一直存在一条强引用链:

Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value

永远无法回收,造成内存泄漏。这样看,似乎是弱引用导致了内存泄漏?

事实上是,无论这里使用强引用还是弱引用,都有可能造成内存泄漏。如果key 使用强引用:引用的ThreadLocal的对象如果置为null,被回收了,但是ThreadLocalMap还持有ThreadLocal的强引用,如果没有手动删除,ThreadLocal不会被回收,导致Entry内存泄漏。

反过来,如果key使用了弱引用,当jvm发现内存不足时,会自动回收弱引用指向的实例内存,也就是回收对ThreadLocal对象。但是这个时候value还是存在的。不过没有关系,看源码你会发行在调用get或者set操作的时候,都有机会执行回收无效entry的操作。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有